import { configure, observable, action, computed, autorun } from 'mobx';
import _ from 'lodash';
import { validate } from 'tcomb-validation';
import { activePickerIOS } from '.';
configure({ enforceActions: true });

class FormStore {
  @observable values = {};
  @observable errors = {};
  @observable fields = {};
  @observable activePicker = {};
  @observable activeDate = {};
  @observable validating = false;
  @action
  init = fields => {
    this.clear();
    try {
      const validFields = _.filter(fields, ({ type }) => type !== 'Title');
      this.setFields(validFields);
      this.setInitialValues(validFields);
      this.setErrors();
    } catch (error) {
      global.bugsnag.notify(error, (report) => {
        report.metadata = {
          "module": {
            "name": "Former.model.init",
          }
        }
      })
    }
  };
  @action
  setFields = fields => {
    this.fields = fields;
  };
  @action
  setErrors = (errors = {}) => {
    this.errors = errors;
  };
  @action
  setInitialValues = fields => {
    const values = {};
    for (let field of fields) {
      values[field.key] = field.initialValue;
    }
    this.values = values;
  };
  getValues(keys) {
    if (_.isArray(keys)) {
      const result = {};
      _.map(keys, key => {
        result[key] = this.value[key];
      });
      return result;
    }
    return this.values;
  }

  @action
  setValues(newValues) {
    try {
      if (!_.isPlainObject(newValues)) {
        throw new Error('the first argument type must be plain Object');
      }
      const oldValues = this.getValues();
      this.getValues();
      this.values = { ...oldValues, ...newValues };
      const oldErrors = this.getErrors();
      const keys = _.keys(newValues);
      for (let key of keys) {
        delete oldErrors[key];
      }
      this.errors = { ...oldErrors };
    } catch (error) {
      global.bugsnag.notify(error, (report) => {
        report.metadata = {
          "module": {
            "name": "Former.model.setValues",
          }
        }
      })
    }
  }
  getValue(key) {
    try {
      if (key === undefined) {
        throw new Error('the first argument "key" is undefined');
      }
      return this.values[key];

    } catch (error) {
      global.bugsnag.notify(error, (report) => {
        report.metadata = {
          "module": {
            "name": "Former.model.getValue",
          }
        }
      })
    }
  }
  @action
  setValue(key, value) {
    try {
      if (key === undefined) {
        throw new Error('the first argument "key" is undefined');
      }
      const oldValues = this.getValues();
      this.values = { ...oldValues, [key]: value };
      const oldErrors = this.getErrors();
      delete oldErrors[key];
      this.errors = { ...oldErrors };

    } catch (error) {
      global.bugsnag.notify(error, (report) => {
        report.metadata = {
          "module": {
            "name": "Former.model.setValue",
          }
        }
      })

    }
  }
  getErrors = () => {
    return this.errors;
  };
  @computed
  get hasError() {
    return !_.isEmpty(this.errors);
  }
  getField(key) {
    return this.fields[key];
  }
  setValidateError = (error, newErr) => {
    if (!_.isArray(error)) {
      error = [];
    }
    error.push(newErr);
    return error;
  };
  @action
  validate = (...args) => {
    this.validating = true;
    switch (true) {
      case args.length < 1:
      case args.length === 1 && !_.isFunction(args[0]):
      case args.length > 1 && !_.isFunction(args[1]): {
        throw new Error(
          'please defined the callback to execute the business after this validation, the last argument must be the callback'
        );
      }
      case args.length > 1 && !_.isArray(args[0]): {
        throw new Error(
          'if you want some values of the form, you must defined the first argument as type Array'
        );
      }
    }
    let keys, cb;
    if (args.length === 1) {
      keys = _.map(this.fields, ({ key }) => key);
      cb = args[0];
    }
    if (args.length > 1) {
      keys = _.isEmpty(args[0])
        ? _.map(this.fields, ({ key }) => key)
        : args[0];
      cb = args[1];
    }
    const _fields = _.keyBy(this.fields, 'key');
    
    const _errors = {};
    const _values = {};
    for (let key of keys) {
      const target = _fields[key];
      const currentValue = this.getValue(key);
      if (target) {
        const rules = target.rules;
        const name = target.label;
        if (rules && rules.length > 0) {
          for (let { validation, message, require, max, min } of rules) {
            
            if (_.isFunction(validation)) {
              validation(currentValue, msg => {
                if (msg) {
                  _errors[key] = this.setValidateError(_errors[key], {
                    value: currentValue,
                    message: msg,
                    name
                  });
                }
              });
              /* 有自定义校验方法时，忽略内置校验方法 */
              continue;
            }
            if (require === true && !_.trim(currentValue)) {
              _errors[key] = this.setValidateError(_errors[key], {
                value: currentValue,
                message: message || '必填项',
                name
              });
            }
          }
        }
      }
      _values[key] = currentValue;
    }
    if (_errors && !_.isEmpty(_errors)) {
      cb(_errors, null);
      this.errors = _errors;
    } else {
      cb(null, _values);
    }
    this.validating = false;
  };
  @action
  activePickerIOS = (args) => {
    this.activePicker = {
      ...args,
      value: this.getValue(args.key) || args.value
    }
    this.dismissDateIOS()
  }
  @action
  dismissPickerIOS = () => {
    if (!_.isEmpty(this.activePicker)) {
      this.activePicker = {}
    }
  }
  @action
  activeDateIOS = (args) => {
    this.activeDate = {
      ...args,
      value: this.getValue(args.key) || args.value
    }
    this.dismissPickerIOS()
  }
  @action
  dismissDateIOS = () => {
    if (!_.isEmpty(this.activeDate)) {
      this.activeDate = {}
    }
  }
  @action
  clear = () => {
    this.values = {};
    this.errors = {};
    this.fields = {};
    this.activePicker = {};
    this.activeDate = {};
    this.validating = false;
  }
}

export default new FormStore();
